﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web;
//using System.Data.Linq;
/// <summary>The GenericExtenders class</summary>
/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:46 GMT"/>
[DebuggerNonUserCode]
[DebuggerStepThrough]
public static class GenericExtenders
{
	/// <summary>Returns the string as HtmlString so it won't be encoded</summary>
	/// <param name="html">The string to be returned</param>
	/// <created author="laurentiu.macovei" date="Tue, 05 Apr 2011 18:02:57 GMT"/>
	public static HtmlString ToHtml(this string html)
	{
		return html == null ? null : new HtmlString(html);
	}
	/// <summary>Concatenates two sequences.</summary>
	/// <param name="source">The source where to concatenate the intems on</param>
	/// <param name="items">The items to be concatenated</param>
	/// <created author="laurentiu.macovei" date="Mon, 04 Apr 2011 00:24:06 GMT"/>
	[DebuggerStepThrough]
	public static IEnumerable<T> Concat<T>(this IEnumerable<T> source, params T[] items)
	{
		if (source == null)
			return null;
		if (items == null || items.Length == 0)
			return source;
		return source.Concat((IEnumerable<T>)items);
	}
	/// <summary>Returns an array with the members of the object sent.</summary>
	/// <param name="item">The item to be wrapped in an array</param>
	/// <created author="tudor.dumitriu" date="Tue, 15 Mar 2011 12:24:38 GMT"/>
	[DebuggerStepThrough]
	public static T[] AsArray<T>(this T item)
    {
        return new T[] {item};
    }

	/// <summary>Returns an array with the members of the object sent.</summary>
	/// <param name="item">The item to be wrapped as an IEnumerable&lt;<typeparamref name="T"/>&gt;</param>
	[DebuggerStepThrough]
	public static IEnumerable<T> ToEnumerable<T>(this T item)
	{
		yield return item;
	}

	/// <summary>Applies the where expression only if not null</summary>
	/// <param name="query">The query to apply the where expression</param>
	/// <param name="expression">The expression to be applied as where clause. It's safe to be null</param>
	/// <created author="laurentiu.macovei" date="Sat, 26 Mar 2011 02:37:27 GMT"/>
	[DebuggerStepThrough]
	public static IQueryable<T> WhereBy<T>(this IQueryable<T> query, Expression<Func<T, bool>> expression)
	{
		if (query == null || expression == null)
			return query;
		return query.Where(expression);
	}
	
	////
	//// Generating sequences
	////
	//public static IEnumerable<T> Generate<T>(Func<T> generator) where T : class
	//{
	//    if (generator == null) throw new ArgumentNullException("generator");

	//    T t;
	//    while ((t = generator()) != null)
	//    {
	//        yield return t;
	//    }
	//}

	//public static IEnumerable<T> Generate<T>(Func<Nullable<T>> generator) where T : struct
	//{
	//    if (generator == null) throw new ArgumentNullException("generator");

	//    Nullable<T> t;
	//    while ((t = generator()).HasValue)
	//    {
	//        yield return t.Value;
	//    }
	//}

	//public static IEnumerable<T> FromEnumerator<T>(IEnumerator<T> enumerator)
	//{
	//    if (enumerator == null) throw new ArgumentNullException("enumerator");

	//    while (enumerator.MoveNext())
	//    {
	//        yield return enumerator.Current;
	//    }
	//}

	//public static IEnumerable<T> Single<T>(T value)
	//{
	//    return Enumerable.Repeat(value, 1);
	//}

	////
	//// I/O
	////
	//public static IEnumerable<string> ReadLinesFromFile(string path)
	//{
	//    if (path == null) throw new ArgumentNullException("path");
	//    using (StreamReader file = new StreamReader(path))
	//    {
	//        string line;
	//        while ((line = file.ReadLine()) != null) yield return line;
	//    }
	//}

	//public static IEnumerable<string> ReadLinesFromConsole()
	//{
	//    return ReadLinesFrom(Console.In);
	//}

	//public static IEnumerable<string> ReadLinesFrom(TextReader reader)
	//{
	//    if (reader == null) throw new ArgumentNullException("reader");

	//    return Generate(() => reader.ReadLine());
	//}

	//public static void WriteLinesTo<T>(this IEnumerable<T> lines, TextWriter writer)
	//{
	//    if (lines == null) throw new ArgumentNullException("lines");
	//    if (writer == null) throw new ArgumentNullException("writer");

	//    lines.ForEach((line) => writer.WriteLine(line.ToString()));
	//}

	//public static void WriteLinesToConsole<T>(this IEnumerable<T> lines)
	//{
	//    lines.WriteLinesTo(Console.Out);
	//}

	//public static void WriteLinesToFile<T>(this IEnumerable<T> lines, string path)
	//{
	//    if (path == null) throw new ArgumentNullException("path");

	//    using (TextWriter file = new StreamWriter(path))
	//    {
	//        lines.WriteLinesTo(file);
	//    }
	//}

	/// <summary>
	/// Determines whether a sequence contains a specified element by using the default
	/// equality comparer.
	/// </summary>
	/// <param name="source"></param>
	/// <param name="list"></param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	[DebuggerStepThrough]
	public static bool In<T>(this T source, params T[] list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Contains(source);
	}
	/// <summary>
	/// Determines whether a sequence contains a specified element by using the default
	/// equality comparer.
	/// </summary>
	/// <param name="source">The value to locate in the sequence.</param>
	/// <param name="list">A sequence in which to locate a value.</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	public static bool In<T>(this T source, IEnumerable<T> list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Contains(source);
	}
	/// <summary>
	/// Determines whether a sequence contains a specified element by using a specified
	/// System.Collections.Generic.IEqualityComparer&lt;<typeparamref name="T"/>&gt;.
	/// </summary>
	/// <param name="source">The value to locate in the sequence.</param>
	/// <param name="comparer">An equality comparer to compare values.</param>
	/// <param name="list">A sequence in which to locate a value.</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	[DebuggerStepThrough]
	public static bool In<T>(this T source, IEqualityComparer<T> comparer, params T[] list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Contains(source, comparer);
	}
	/// <summary>
	/// Determines whether a sequence contains a specified element by using a specified
	/// System.Collections.Generic.IEqualityComparer&lt;<typeparamref name="T"/>&gt;.
	/// </summary>
	/// <param name="source">The value to locate in the sequence.</param>
	/// <param name="comparer">An equality comparer to compare values.</param>
	/// <param name="list">A sequence in which to locate a value.</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	public static bool In<T>(this T source, IEqualityComparer<T> comparer, IEnumerable<T> list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Contains(source, comparer);
	}

	/// <summary>
	/// Determines whether a sequence contains a specified element by using the default
	/// equality comparer.
	/// </summary>
	/// <param name="source"></param>
	/// <param name="list"></param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	[DebuggerStepThrough]
	public static bool In<T>(this IEnumerable<T> source, params T[] list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Any(t => list.Contains(t));
	}
	/// <summary>
	/// Determines whether a sequence contains a specified element by using the default
	/// equality comparer.
	/// </summary>
	/// <param name="source">The value to locate in the sequence.</param>
	/// <param name="list">A sequence in which to locate a value.</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	public static bool In<T>(this IEnumerable<T> source, IEnumerable<T> list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Any(t => list.Contains(t));
	}
	/// <summary>
	/// Determines whether a sequence contains a specified element by using a specified
	/// System.Collections.Generic.IEqualityComparer&lt;<typeparamref name="T"/>&gt;.
	/// </summary>
	/// <param name="source">The value to locate in the sequence.</param>
	/// <param name="comparer">An equality comparer to compare values.</param>
	/// <param name="list">A sequence in which to locate a value.</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	public static bool In<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer, params T[] list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Any(t => list.Contains(t));
	}
	/// <summary>
	/// Determines whether a sequence contains a specified element by using a specified
	/// System.Collections.Generic.IEqualityComparer&lt;<typeparamref name="T"/>&gt;.
	/// </summary>
	/// <param name="source">The value to locate in the sequence.</param>
	/// <param name="comparer">An equality comparer to compare values.</param>
	/// <param name="list">A sequence in which to locate a value.</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	public static bool In<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer, IEnumerable<T> list)
	{
		if (null == source) throw new ArgumentNullException("source");
		return list.Any(t => list.Contains(t));
	}
	//
	// Side effects
	//
	/// <summary>
	/// </summary>
	/// <param name="source"></param>
	/// <param name="action"></param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	[DebuggerStepThrough]
	public static IEnumerable<T> Do<T>(this IEnumerable<T> source, Action<T> action)
	{
		if (source == null)
			yield break;
		if (action == null)
			yield break;

		foreach (T elem in source)
		{
			action(elem);
			yield return elem;
		}
	}

	/// <summary>Executes an action for every item in the collection</summary>
	/// <param name="source">The collection to be enumerated</param>
	/// <param name="action">The action with the item and the index to be executed for each item in the collection</param>
	/// <created author="laurentiu.macovei" date="Sun, 13 Mar 2011 21:14:47 GMT"/>
	[DebuggerStepThrough]
	public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
	{
		if (source == null || action == null)
			return;

		foreach (T elem in source)
			action(elem);
	}

	/// <summary>Executes an action for every item in the collection. The action gets the item and its zero-based index</summary>
	/// <param name="source">The collection to be enumerated</param>
	/// <param name="action">The action with the item and the index to be executed for each item in the collection</param>
	/// <created author="laurentiu.macovei" date="Wed, 06 Apr 2011 13:42:58 GMT"/>
	[DebuggerStepThrough]
	public static void ForEach<T>(this IEnumerable<T> source, Action<T, int> action)
	{
		if (source == null || action == null)
			return;
		var index = 0;
		foreach (T elem in source)
			action(elem, index++);
	}

	////
	//// ToStringPretty
	////
	//public static string ToStringPretty<T>(this IEnumerable<T> source)
	//{
	//    return ToStringPretty(source, ",");
	//}

	//public static string ToStringPretty<T>(this IEnumerable<T> source, string delimiter)
	//{
	//    return ToStringPretty(source, "", delimiter, "");
	//}

	//public static string ToStringPretty<T>(this IEnumerable<T> source, string before, string delimiter, string after)
	//{
	//    if (source == null) throw new ArgumentNullException("source");

	//    StringBuilder result = new StringBuilder();
	//    result.Append(before);

	//    bool firstElement = true;
	//    foreach (T elem in source)
	//    {
	//        if (firstElement) firstElement = false;
	//        else result.Append(delimiter);

	//        result.Append(elem.ToString());
	//    }

	//    result.Append(after);
	//    return result.ToString();
	//}

	////
	//// Other
	////
	//public static IEnumerable<TOut> Combine<TIn1, TIn2, TOut>(
	//    this IEnumerable<TIn1> in1, IEnumerable<TIn2> in2, Func<TIn1, TIn2, TOut> func)
	//{
	//    if (in1 == null) throw new ArgumentNullException("in1");
	//    if (in2 == null) throw new ArgumentNullException("in2");
	//    if (func == null) throw new ArgumentNullException("func");

	//    using (var e1 = in1.GetEnumerator())
	//    using (var e2 = in2.GetEnumerator())
	//    {
	//        while (e1.MoveNext() && e2.MoveNext())
	//        {
	//            yield return func(e1.Current, e2.Current);
	//        }
	//    }
	//}

	//public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
	//{
	//    return Shuffle(source, new Random());
	//}

	//public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random random)
	//{
	//    if (source == null) throw new ArgumentNullException("source");
	//    if (random == null) throw new ArgumentNullException("random");

	//    T[] array = source.ToArray();

	//    for (int i = 0; i < array.Length; i++)
	//    {
	//        int r = random.Next(i + 1);
	//        T tmp = array[r];
	//        array[r] = array[i];
	//        array[i] = tmp;
	//    }

	//    return array;
	//}

	/// <summary>Returns a string array that contains the substrings in this string that are delimited by elements of a specified Unicode character array. A parameter specifies whether to return empty array elements.</summary>
	/// <param name="value">The string value to be split</param>
	/// <param name="options">Specify System.StringSplitOptions.RemoveEmptyEntries to omit empty array elements from the array returned, or System.StringSplitOptions.None to include empty array elements in the array returned.</param>
	/// <param name="separators">An array of Unicode characters that delimit the substrings in this string, an empty array that contains no delimiters, or null.</param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:51:23 GMT"/>
	public static string[] Split(this string value, StringSplitOptions options, params char[] separators)
	{
		if (value == null)
			return null;
		return value.Split(separators, options);
	}
	/// <summary>Returns a string array that contains the substrings in this string that are delimited by elements of a specified Unicode character. A parameter specifies whether to return empty array elements.</summary>
	/// <param name="value">The string value to be split</param>
	/// <param name="separator">An Unicode character that delimit the substrings in this string</param>
	/// <param name="options">Specify System.StringSplitOptions.RemoveEmptyEntries to omit empty array elements from the array returned, or System.StringSplitOptions.None to include empty array elements in the array returned.</param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:51:23 GMT"/>
	public static string[] Split(this string value, char separator, StringSplitOptions options)
	{
		return Split(value, options, separator);
	}

	/// <summary>Returns a string array that contains the substrings in this instance that are delimited by elements of a specified Unicode character. A parameter specifies the maximum number of substrings to return.</summary>
	/// <param name="value">The string value to be split</param>
	/// <param name="separator">An Unicode character that delimit the substrings in this string</param>
	/// <param name="count">The maximum number of substrings to return.</param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:56:28 GMT"/>
	public static string[] Split(this string value, char separator, int count)
	{
		return Split(value, count, separator);
	}
	/// <summary>Returns a string array that contains the substrings in this instance that are delimited by elements of a specified Unicode character array. A parameter specifies the maximum number of substrings to return.</summary>
	/// <param name="value">The string value to be split</param>
	/// <param name="separators">An array of Unicode characters that delimit the substrings in this string, an empty array that contains no delimiters, or null.</param>
	/// <param name="count">The maximum number of substrings to return.</param>
	/// <returns></returns>
	/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:56:28 GMT"/>
	public static string[] Split(this string value, int count, params char[] separators)
	{
		if (value == null)
			return null;
		return value.Split(separators, count);
	}
	/// <summary>Extends the given extender with the provided extendees by updating its properties</summary>
	/// <param name="extender">The source where all the extendees options will be added</param>
	/// <param name="extendees">Optional more extendees to be added</param>
	/// <returns>The given extender</returns>
	/// <created author="laurentiu.macovei" date="Fri, 05 Mar 2010 17:52:48 GMT"/>
	public static IDictionary MergeWith(this IDictionary extender, params IDictionary[] extendees)
	{
		return MergeWith(extender, (IEnumerable<IDictionary>)extendees);
	}

	/// <summary>Extends the given extender with the provided extendees by updating its properties</summary>
	/// <param name="extender">The source where all the extendees options will be added</param>
	/// <param name="extendees">An enumerable list of extendees to be added</param>
	/// <returns>The given extender</returns>
	/// <created author="laurentiu.macovei" date="Fri, 05 Mar 2010 17:52:48 GMT"/>
	public static IDictionary MergeWith(this IDictionary extender, IEnumerable<IDictionary> extendees)
	{
		foreach (var dict in extendees)
			foreach (var key in dict.Keys)
			{
				var value = dict[key];
				if (value is IDictionary)
				{
					if (extender.Contains(key))
					{
						var oldValue = extender[key];
						if (oldValue is IDictionary)
						{
							MergeWith((IDictionary)oldValue, (IDictionary)value);
							continue;
						}
					}
					value = ((IDictionary)Activator.CreateInstance(value.GetType())).MergeWith((IDictionary)value);
				}
				extender[key] = value;
			}
		return extender;
	}

	/// <summary>Extends the given extender with the provided extendees by updating its properties</summary>
	/// <param name="extender">The source where all the extendees options will be added</param>
	/// <param name="extendees">Optional more extendees to be added</param>
	/// <returns>The given extender</returns>
	/// <created author="laurentiu.macovei" date="Fri, 05 Mar 2010 17:52:48 GMT"/>
	public static IDictionary<K,V> MergeWith<K,V>(this IDictionary<K,V> extender, params IDictionary<K,V>[] extendees)
	{
		return MergeWith(extender, (IEnumerable<IDictionary<K,V>>)extendees);
	}

	/// <summary>Extends the given extender with the provided extendees by updating its properties</summary>
	/// <param name="extender">The source where all the extendees options will be added</param>
	/// <param name="extendees">An enumerable list of extendees to be added</param>
	/// <returns>The given extender</returns>
	/// <created author="laurentiu.macovei" date="Fri, 05 Mar 2010 17:52:48 GMT"/>
	public static IDictionary<K,V> MergeWith<K,V>(this IDictionary<K,V> extender, IEnumerable<IDictionary<K,V>> extendees)
	{
		foreach (var dict in extendees)
			if (dict != null)
			foreach (var key in dict.Keys)
			{
				var value = dict[key];
				if (value is IDictionary<K,V>)
				{
					if (extender.ContainsKey(key))
					{
						var oldValue = extender[key];
						if (oldValue is IDictionary<K,V>)
						{
							MergeWith((IDictionary<K,V>)oldValue, (IDictionary<K,V>)value);
							continue;
						}
						if (oldValue is IDictionary)
						{
							MergeWith((IDictionary)oldValue, (IDictionary)value);
							continue;
						}
					}
//					value = ((IDictionary<K,V>)Activator.CreateInstance(value.GetType())).MergeWith((IDictionary<K,V>)value);
				}
				extender[key] = value;
			}
		return extender;
	}

	//public static IEnumerable<T> ExecuteQuery<T>(this DataContext context, T typeInference, string query, params object[] parameters)
	//{
	//    return context.ExecuteQuery<T>(query, parameters);
	//}

}
